/*
  S.M.A.C.K - An operating system kernel
  Copyright (C) 2010,2011 Mattias Holm and Kristian Rietveld
  For licensing and a full list of authors of the kernel, see the files
  COPYING and AUTHORS.
*/

#include <interrupt.h>

#include <stdint.h>
#include <stdio.h>
#include <timer.h>
#include <vm.h>

void
disable_interrupts(void)
{
  __asm__ volatile (
    "mrs r4, CPSR\n"
    "orr r4, r4, #0x80\n"
    "msr CPSR, r4\n"
    : // No outs
    : // No ins
    : "r4"
    );
}

void
disable_fast_interrupts(void)
{
  __asm__ volatile (
    "mrs r4, CPSR\n"
    "orr r4, r4, #0x40\n"
    "msr CPSR, r4\n"
    : // No outs
    : // No ins
    : "r4"
    );
}

void
enable_interrupts(void)
{
  __asm__ volatile (
    "mrs r4, CPSR\n"
    "bic r4, r4, #0x80\n"
    "msr CPSR, r4\n"
    : // No outs
    : // No ins
    : "r4"
    );
}

void
enable_fast_interrupts(void)
{
  __asm__ volatile (
    "mrs r4, CPSR\n"
    "bic r4, r4, #0x40\n"
    "msr CPSR, r4\n"
    : // No outs
    : // No ins
    : "r4"
    );
}

uint32_t
save_flags(void)
{
  uint32_t reg;

  __asm__ volatile (
    "mrs %[reg], CPSR\n"
    "ldr r5, =0x3ff\n"
    "and %[reg], %[reg], r5\n"
    : [reg] "=r" (reg) // Out
    : //No ins
    : "r5"
    );

  return reg;
}

void
restore_flags(uint32_t flags)
{
  __asm__ volatile (
    "mrs r4, CPSR\n"
    "ldr r5, =0x3ff\n"
    "bic r4, r4, r5\n"
    "orr r4, r4, %[flags]\n"
    "msr CPSR, r4\n"
    : // No outs
    : [flags] "r" (flags) // In
    : "r4", "r5"
    );
}

/* FIXME: This is likely to be moved to another file to be together
 * with other barriers.
 */
void
data_sync_barrier(void)
{
  __asm__ volatile (
    "mov r0, #0\n"
    "mcr p15, #0, r0, c7, c10, #4\n"
    : // No outs
    : // No ins
    : "r0"
    );
}

struct intcps
{
  uint32_t revision;
  uint32_t padding0[3];
  uint32_t sysconfig;
  uint32_t sysstatus;
  uint32_t padding1[10];
  uint32_t sir_irq;
  uint32_t sir_fiq;
  uint32_t control;
  uint32_t protection;
  uint32_t idle;
  uint32_t padding2[3];
  uint32_t irq_priority;
  uint32_t fiq_priority;
  uint32_t threshold;
  uint32_t padding3[5];
  struct
    {
      uint32_t itr;
      uint32_t mir;
      uint32_t mir_clear;
      uint32_t mir_set;
      uint32_t isr_set;
      uint32_t isr_clear;
      uint32_t pending_irq;
      uint32_t pending_fiq;
    }
  banks[3];
};

static struct intcps *intcps;

#define INTCPS_CONTROL_NEWIRQAGR 0


void
hw_interrupt_init(void)
{
  intcps = vm_map_physical(vm_get_kernel_map(VM_REG_DEVICE), VM_DEVICE | VM_SUPER_RW,
                           (uintptr_t)0x48200000, 4096);
}

void
unmask_interrupt(unsigned int irq)
{
  unsigned int bank;
  unsigned int bit;

  bank = irq / 32;
  bit = irq % 32;

  /* Clear interrupt mask bit. */
  intcps->banks[bank].mir_clear |= 1 << bit;
}

void
mask_interrupt(unsigned int irq)
{
  unsigned int bank;
  unsigned int bit;

  bank = irq / 32;
  bit = irq % 32;

  /* Set interrupt mask bit. */
  intcps->banks[bank].mir_set |= 1 << bit;
}

void
interrupt(void)
{
  unsigned int active_irq;

  /* Determine active IRQ */
  active_irq = intcps->sir_irq & 0x7f;

  interrupt_call_handler(active_irq);

  /* Request next interrupt */
  intcps->control = 1 << INTCPS_CONTROL_NEWIRQAGR;

  data_sync_barrier();
}
